"""发票明细"""

from datetime import datetime

from odoo import models, api, fields
from odoo.tools import float_compare
from odoo.exceptions import UserError, ValidationError
from odoo.addons import decimal_precision as dp
from odoo.tools import float_compare


class AccountInvoice(models.Model):
    _inherit = 'account.invoice'

    nd_total_hc = fields.Boolean('此发票被整张红冲')
    pre_hc_account_line_ids = fields.Many2many('pre.account.line', string="预发票红冲明细")

    @api.multi
    def unlink(self):
        """当删除主表时，明细行account.invoice.line应该是通过数据库的ondelete指令进行删除的，
        而不是在ORM层进行的，所以预发票明细行相关的compute方法不会触发，这里需要手动触发"""
        # 对于那些红冲发票,删除后需要恢复原发票的nd_total_hc为False,恢复原发票明细状态为已开票
        for invoice in self.filtered(lambda x: x.type in ['out_refund', 'in_refund']):
            origin_invoice_id = invoice.refund_invoice_id
            origin_invoice_id.nd_total_hc = False
            origin_invoice_lines = origin_invoice_id.mapped('invoice_line_ids.pre_account_line_id')
            origin_invoice_lines._update_state(reverse=True)

        pre_invoice_lines = self.mapped('invoice_line_ids.pre_account_line_id')
        for invoice in self:
            if float_compare(invoice.amount_total, 0, 2) > 0:
                invoice.update_pre_invoice_line_state(invoice.mapped('invoice_line_ids.pre_account_line_id'))
        res = super().unlink()
        if pre_invoice_lines:
            line_model = self.env['pre.account.line']
            need_recompute_fields = ['invoiced_amount', 'uninvoiced_amount', 'invoiced_qty',
                                     'invoiced_qty_done', 'account_invoice_count']
            for field in need_recompute_fields:
                self.env.add_todo(line_model._fields[field], pre_invoice_lines)
            line_model.recompute()
            # self.update_pre_invoice_line_state(pre_invoice_lines)

        return res

    @api.multi
    def action_invoice_cancel(self):
        """取消发票的同时,需要将原发票明细恢复至可开票状态"""
        res = super().action_invoice_cancel()
        # pre_invoice_lines = self.mapped('invoice_line_ids.pre_account_line_id')
        # self.update_pre_invoice_line_state(pre_invoice_lines)
        for invoice in self:
            if float_compare(invoice.amount_total, 0, 2) > 0:
                invoice.update_pre_invoice_line_state(invoice.mapped('invoice_line_ids.pre_account_line_id'))
            if invoice.type in ['out_refund', 'in_refund'] and invoice.pre_hc_account_line_ids:
                invoice.pre_hc_account_line_ids.write({"state": 'partly'})
            if invoice.type in ['out_refund', 'in_refund'] and invoice.refund_invoice_id:
                invoice.refund_invoice_id.write({"nd_total_hc": False})
        return res

    @api.multi
    def action_invoice_draft(self):
        """禁止重新编辑发票"""
        if self._context.get('allow_invoice_reset'):
            return super().action_invoice_draft()
        else:
            raise UserError('禁止将被取消的发票转为草稿状态!')

    def update_pre_invoice_line_state(self, lines):
        """将发票明细回退到可开票状态,用于发票被删除,取消等情况下更新发票明细状态"""
        # for line in lines:
            # if line.uninvoiced_amount > 0:
            #     line.state = 'partly'
        lines.write({'state': 'partly'})

    def write(self, val):
        res = super().write(val)
        return res


class AccountInvoiceLine(models.Model):
    _inherit = 'account.invoice.line'

    pre_account_line_id = fields.Many2one('pre.account.line', string="预发票明细")
    nd_total_hc = fields.Boolean('此发票被整张红冲', related="invoice_id.nd_total_hc", store=True)


class StockMove(models.Model):
    _inherit = 'stock.move'

    pre_account_line_ids = fields.One2many('pre.account.line', 'move_id', string="已生成的预发票明细")

    def _is_return(self):
        """判断此stock.move是否是退货的，如果是销售单产生的，但picking_code却是'incoming'，
        采购单产生的，是'outgoing'，符合此规则的则认为是退货"""
        self.ensure_one()
        if self.sale_line_id and self.picking_code == 'incoming':
            return True
        elif self.purchase_line_id and self.picking_code == 'outgoing':
            return True
        else:
            return False


class SaleLine(models.Model):
    _inherit = 'sale.order.line'

    pre_account_line_ids = fields.One2many('pre.account.line', 'sale_line_id', string="已生成的预发票明细")

    def can_price_updated(self, update_date=None):
        """检查价格是否可以被更改，不能跨越、已开票的不能"""
        self.ensure_one()
        if not update_date:
            update_date = fields.Date.today()
        first_day_of_update_month = update_date.replace(day=1)
        if self.order_id.confirmation_date:
            confirmation_date = fields.Date.from_string(self.order_id.confirmation_date)
        else:
            confirmation_date = fields.Date.from_string(self.create_date)
        if confirmation_date < first_day_of_update_month:
            return False

        account_invoice_lines = self.mapped('pre_account_line_ids.account_move_line_id')
        if account_invoice_lines.filtered(lambda x: x.invoice_id.state not in ['cancel']):
            return False
        return True

    def write(self, val):
        """发生价格变更后，修改发票明细上的价格信息"""
        need_update = 'price_unit' in val or 'tax_id' in val
        if need_update:
            pass
        res = super().write(val)

        if need_update:
            for line in self:
                if not line.can_price_updated():
                    raise UserError('此销售明细已经开票或者已跨月，不能修改价格！')
                total_quantity = line.product_uom_qty
                for pre_line in line.pre_account_line_ids:
                    quantity = pre_line.measure_unit._compute_quantity(pre_line.quantity, line.product_uom)
                    percent = quantity / total_quantity
                    pre_line.write({
                        'avg_price': line.price_unit,
                        'business_amount': line.price_total * percent,
                        'business_untaxed_amount': line.price_subtotal * percent,
                        'business_tax': line.price_tax * percent,
                        'tax_id': [(6, 0, line.tax_id.ids)],
                    })
        return res


class PurchaseLine(models.Model):
    _inherit = 'purchase.order.line'

    pre_account_line_ids = fields.One2many('pre.account.line', 'purchase_line_id', string="已生成的预发票明细")


class PreAccountLine(models.Model):
    _name = 'pre.account.line'
    _description = '预发票明细'

    name = fields.Char('名称', readonly=True)
    type = fields.Selection([
        ('sale', '销售'),
        ('purchase', '采购'),
        ('sale_return', '销售退货'),
        ('purchase_return', '采购退货')], string="发票类型", copy=False, required=True)
    # 发票明细可能是根据采购明细、销售明细、调拨明细生成的
    move_id = fields.Many2one('stock.move', string="调拨明细")
    sale_line_id = fields.Many2one('sale.order.line', string="销售明细")
    purchase_line_id = fields.Many2one('purchase.order.line', string="采购明细")
    order_sequence_num = fields.Integer('顺序序号', compute="_compute_order_sequence_num", store=True)

    account_move_line_id = fields.One2many('account.invoice.line', 'pre_account_line_id', string="发票明细")
    partner_id = fields.Many2one('res.partner', string='客户名')
    product_id = fields.Many2one('product.product', string='产品')
    product_default_code = fields.Char('产品编码', store=True, related="product_id.default_code")

    # 销售或者采购的价格信息，从采购或者销售单上读取
    quantity = fields.Float('总数量')
    business_untaxed_amount = fields.Float('销售/采购金额(不含税)')
    business_tax= fields.Float('销售/采购税额')
    business_amount = fields.Float('销售/采购金额(含税)')
    tax_id = fields.Many2many('account.tax', string='税率')
    avg_price = fields.Float('含税单价')
    measure_unit = fields.Many2one('uom.uom', string='计量单位')
    business_user_id = fields.Many2one('res.users', string="业务人员")

    # 已开票信息
    invoiced_amount = fields.Float('已开票金额（不含税）', store=True, compute="_compute_invoice_amount")
    uninvoiced_amount = fields.Float('未开票金额（不含税）', store=True, compute="_compute_invoice_amount")
    invoiced_amount_tax = fields.Float('已开票金额（含税）', store=True, compute="_compute_invoice_amount")
    uninvoiced_amount_tax = fields.Float('未开票金额（含税）', store=True, compute="_compute_invoice_amount")
    invoiced_qty = fields.Float('已开票数量', store=True, compute="_compute_invoiced_qty")
    uninvoiced_qty = fields.Float('未开票数量', store=True, compute="_compute_invoiced_qty")
    invoiced_qty_done = fields.Float('已确认的开票数量',  store=True, compute="_compute_invoiced_qty")
    account_invoice_count = fields.Integer('单据数量', store=True, compute="_compute_account_invoice_count")

    # 退货和红冲相关信息
    return_quantity = fields.Float('退货数量')
    true_return_quantity = fields.Float('实际退货数量')
    rest_return_quantity = fields.Float('剩余待处理数量')
    total_minus_return = fields.Float('销售总金额(去除退货)')

    ticket_creation_time = fields.Datetime('开票时间')
    part_hc_quantity = fields.Float('部分红冲数量(不包含整单红冲以及取消的)')
    move_create_date = fields.Datetime('出入库单创建时间')
    stock_date = fields.Date('出入库时间', compute="_compute_stock_date", store=True)

    state = fields.Selection([
        ('not_invoiced', '未开票'),
        ('partly', '部分开票'),
        ("invoiced", "已开票"),
        ('free', '无需开票'),
        ('hc', '已红冲')], string="开票状态", copy=False, default="not_invoiced")

    @api.depends('move_id', 'sale_line_id', 'purchase_line_id')
    def _compute_order_sequence_num(self):
        """根据关联的业务单据计算出排序序号，目的是让相同业务单据下面的发票明细处于相邻位置，便于查看操作"""
        for line in self:
            num = 0
            if line.sale_line_id:
                num = line.sale_line_id.id
            elif line.purchase_line_id:
                num = line.purchase_line_id.id
            elif line.move_id:
                if line.move_id.sale_line_id:
                    num = line.move_id.sale_line_id.id
                elif line.move_id.purchase_line_id:
                    num = line.move_id.purchase_line_id.id
            line.order_sequence_num = num

    @api.depends('move_id')
    def _compute_stock_date(self):
        for line in self:
            stock_date = False
            if line.move_id:
                # picking_id = line.move_id.picking_id
                # if picking_id.sale_id:
                #     date = picking_id.sale_id.date_order
                # elif picking_id.purchase_id:
                #     date = picking_id.purchase_id.date_order
                # else:
                #     date = line.move_id.create_date
                # if date:
                #     stock_date = datetime.date(date)
                picking_id = line.move_id.picking_id
                date = False
                if picking_id.date_done:
                    date = picking_id.date_done
                if date:
                    stock_date = datetime.date(date)
            line.stock_date = stock_date

    def _update_state(self, reverse=False):
        """这个方法用来在整张发票红冲之后，更新被红冲发票明细的状态！不能用于其他情况下的状态更新
        reverse参数表示红冲发票被删除了，需要恢复到原来状态"""
        for order in self:
            if reverse:
                if order.uninvoiced_amount <= 0:
                    order.state = 'invoiced'
                else:
                    order.state = 'partly'
            else:
                if order.invoiced_amount <= 0:
                    # order.state = 'not_invoiced'
                    order.state = 'hc'
                else:
                    order.state = 'partly'

    def mark_line_free(self):
        """标记发票明细为“无需开票”"""
        not_invoiced_lines = self.filtered(lambda x: x.state != 'not_invoiced')
        if not_invoiced_lines:
            raise UserError('有发票明细已经开票，不能再标记为“无需开票”！')
        self.write({'state': 'free'})

    @api.model
    def create(self, vals):
        vals['name'] = self.env['ir.sequence'].next_by_code('nd.account.line')
        res = super().create(vals)
        return res

    @api.multi
    def view_account_invoice(self):
        """查看已开发票"""
        self.ensure_one()

        if self.type in ['sale', 'sale_return']:
            action = self.env.ref('account.action_invoice_tree1').read()[0]
        elif self.type in ['purchase', 'purchase_return']:
            action = self.env.ref('account.action_vendor_bill_template').read()[0]
        else:
            raise UserError(f'不能识别的发票明细类型：{self.type}')

        invoice_ids = self.account_move_line_id.mapped('invoice_id')
        if len(invoice_ids) == 1:
            action['res_id'] = invoice_ids.id
            action['views'] = [(False, 'form')]
        else:
            action['domain'] = [('id', 'in', invoice_ids.ids)]

        return action

    # @api.depends('account_move_line_id.price_subtotal', 'account_move_line_id')
    # def _compute_state(self):
    #     """根据已开票金额来判断开票状态，判断不含税金额"""
    #     for order in self:
    #         if not order.account_move_line_id:
    #             order.state = 'not_invoiced'
    #         elif sum(order.account_move_line_id.mapped('price_subtotal')) >= order.business_amount:
    #             order.state = 'invoiced'
    #         else:
    #             order.state = 'partly'

    @api.depends('account_move_line_id')
    def _compute_account_invoice_count(self):
        for order in self:
            order.account_invoice_count = len(order.account_move_line_id.filtered(lambda x: not x.nd_total_hc).mapped('invoice_id'))

    @api.depends('account_move_line_id.price_subtotal', 'account_move_line_id',
                 'account_move_line_id.price_total', 'state',
                 'business_untaxed_amount', 'business_amount',
                 'account_move_line_id.nd_total_hc', 'account_move_line_id.invoice_id.state')
    def _compute_invoice_amount(self):
        """计算已开票金额、未开票金额。整张红冲的发票不计入(要计算未税金额)
        """
        for order in self:
            # filter_domain = lambda inv_line: not inv_line.nd_total_hc and inv_line.invoice_id.state != 'cancel'
            # invoiced_amount = sum(order.account_move_line_id.filtered(filter_domain).mapped('price_subtotal'))
            invoiced_amount = 0
            invoiced_amount_tax = 0
            if order.state == 'invoiced' and order.type in ['sale_return', 'purchase_return']:
                order.update({
                    'invoiced_amount': order.business_untaxed_amount,
                    'uninvoiced_amount': 0,  # 不含税金额
                    'invoiced_amount_tax': order.business_amount,
                    'uninvoiced_amount_tax': 0,
                })
            else:
                for inv_line in order.account_move_line_id:
                    if inv_line.invoice_id.state != 'cancel':
                        if inv_line.invoice_id.type in ['in_refund', 'out_refund']:
                            invoiced_amount -= inv_line.price_subtotal
                            invoiced_amount_tax -= inv_line.price_total
                        else:
                            invoiced_amount += inv_line.price_subtotal
                            invoiced_amount_tax += inv_line.price_total

                order.update({
                    'invoiced_amount': invoiced_amount,
                    'uninvoiced_amount': order.business_untaxed_amount - invoiced_amount,   # 不含税金额
                    'invoiced_amount_tax': invoiced_amount_tax,
                    'uninvoiced_amount_tax': order.business_amount - invoiced_amount_tax,
                })

    @api.depends('account_move_line_id.invoice_id.state', 'account_move_line_id.quantity',
                 'quantity',
                 'account_move_line_id', 'account_move_line_id.nd_total_hc', 'state')
    def _compute_invoiced_qty(self):
        """计算已开票数量，处于已支付状态的视作已确认的开票，其余的视作未确认，已整张红冲的不计入"""
        for order in self:
            # account_move_lines = order.account_move_line_id.filtered(lambda x: not x.nd_total_hc)
            # # quantity = sum(account_move_lines.mapped('quantity'))
            # # confirmed_line = account_move_lines.filtered(lambda x: x.invoice_id.state == 'paid')
            # # confirmed_quantity = sum(confirmed_line.mapped('quantity'))
            # quantity = 0
            # confirmed_quantity = 0
            # for line in account_move_lines:
            #     if line.invoice_id.state != 'cancel':
            #         if line.invoice_id.type in ['in_refund', 'out_refund']:
            #             quantity -= line.quantity
            #         else:
            #             quantity += line.quantity
            #
            # order.update({
            #     'invoiced_qty': quantity,
            #     'uninvoiced_qty': max(order.quantity - quantity, 0),
            #     'invoiced_qty_done': confirmed_quantity,
            # })

            # ---------------
            invoiced_total = 0
            if order.state == 'invoiced' and order.type in ['sale_return', 'purchase_return']:
                order.update({
                    'invoiced_qty': order.quantity,
                    'uninvoiced_qty': 0,
                    'invoiced_qty_done': order.quantity,
                })
            else:
                for inv_line in order.account_move_line_id:
                    if inv_line.invoice_id.state != 'cancel':
                        if inv_line.invoice_id.type in ['in_refund', 'out_refund']:
                            invoiced_total -= inv_line.quantity
                        else:
                            invoiced_total += inv_line.quantity

                order.update({
                    'invoiced_qty': invoiced_total,
                    'uninvoiced_qty': max(order.quantity - invoiced_total, 0),
                    'invoiced_qty_done': invoiced_total,
                })

    @api.model
    def create_pre_account_line(self, move_ids=False, sale_lines=False, purchase_lines=False):
        """
        根据传入的调拨明细，创建对应的预发票明细，
        生成时机受到产品资料上的销售和采购的控制策略影响：
            1，如果是订购数量，就在销售、采购单确认时自动生成，
            2，如果是已交货/收获数量，就在调拨单确认时生成
            3，如果是退货，则一律在调拨单完成时生成
        :param move_ids: stock.move，调拨明细行
        :param sale_lines: sale.order.line，销售明细行
        :param purchase_lines: purchase.order.line 采购明细行
        :return:
        """
        # 筛选出可以生成发票明细的
        move_ids, return_move_ids, sale_lines, purchase_lines = self._filter_proper_lines(move_ids, sale_lines, purchase_lines)

        datas = []
        for move_id in move_ids:
            data = self._extract_data_from_stock_move(move_id=move_id)
            datas.append(data)

        for return_move_id in return_move_ids:
            data = self._extract_data_from_return_stock_move(return_move_id)
            datas.append(data)

        for sale_line in sale_lines:
            data = self._extract_data_from_sale_line(sale_line=sale_line)
            datas.append(data)

        for purchase_line in purchase_lines:
            data = self._extract_data_from_purchase_line(purchase_line=purchase_line)
            datas.append(data)

        pre_account_lines = self.env['pre.account.line'].create(datas)
        return pre_account_lines

    def _extract_data_from_sale_line(self, sale_line):
        sale_line.ensure_one()
        data = {
            'type': 'sale',
            'move_id': False,
            'sale_line_id': sale_line.id,
            'purchase_line_id': False,
            'partner_id': sale_line.order_id.partner_id.id,
            'product_id': sale_line.product_id.id,
            'quantity': sale_line.product_uom_qty,
            'business_untaxed_amount': sale_line.price_subtotal,
            'avg_price': sale_line.price_unit,
            'business_tax': sale_line.price_tax,
            'business_amount': sale_line.price_total,
            'tax_id': [(6, 0, sale_line.tax_id.ids)],
            'measure_unit': sale_line.product_uom.id,
            'business_user_id': sale_line.order_id.user_id.id,
        }
        return data

    def _extract_data_from_purchase_line(self, purchase_line):
        purchase_line.ensure_one()
        data = {
            'type': 'purchase',
            'move_id': False,
            'sale_line_id': False,
            'purchase_line_id': purchase_line.id,
            'partner_id': purchase_line.order_id.partner_id.id,
            'product_id': purchase_line.product_id.id,
            'quantity': purchase_line.product_qty,  # 完成数量
            'business_untaxed_amount': purchase_line.price_subtotal,
            'avg_price': purchase_line.price_unit,
            'business_tax': purchase_line.price_tax,
            'business_amount': purchase_line.price_total,
            'tax_id': [(6, 0, purchase_line.taxes_id.ids)],
            'measure_unit': purchase_line.product_uom.id,
            'business_user_id': purchase_line.order_id.user_id.id,
        }
        return data

    def _extract_data_from_stock_move(self, move_id):
        move_id.ensure_one()
        sale = purchase = False
        if move_id.picking_code == 'incoming':
            purchase = True
            type = 'purchase'
        elif move_id.picking_code == 'outgoing':
            sale = True
            type = 'sale'
        elif move_id.picking_code == 'internal':
            raise UserError('内部调拨单据不能生成发票明细')
        else:
            raise UserError(f'不能识别的调拨方式：{move_id.picking_code}！')

        taxed_amount, tax, amount, measure_unit, user_id, tax_ids, price_unit = self._get_business_data(move_id, sale=sale, purchase=purchase)

        data = {
            'type': type,
            'move_id': move_id.id,
            'sale_line_id': move_id.sale_line_id.id,
            'purchase_line_id': move_id.purchase_line_id.id,
            'partner_id': self._get_partner_id(move_id, sale=sale, purchase=purchase).id,
            'product_id': move_id.product_id.id,
            'quantity': move_id.quantity_done,  # 完成数量
            'business_untaxed_amount': amount,
            'avg_price': price_unit,
            'business_tax': tax,
            'business_amount': taxed_amount,
            'tax_id': [(6, 0, tax_ids.ids)],
            'measure_unit': measure_unit.id,
            'business_user_id': user_id.id,
        }
        return data

    def _extract_data_from_return_stock_move(self, return_move_id):
        return_move_id.ensure_one()
        sale = purchase = False
        # 要注意，退货和正常的调拨明细是相反的
        if return_move_id.picking_code == 'incoming':
            sale = True
            type = 'sale_return'
        elif return_move_id.picking_code == 'outgoing':
            purchase = True
            type = 'purchase_return'
        elif return_move_id.picking_code == 'internal':
            raise UserError('内部调拨单据不能生成发票明细')
        else:
            raise UserError(f'不能识别的调拨方式：{return_move_id.picking_code}！')

        taxed_amount, tax, amount, measure_unit, user_id, tax_ids, price_unit = self._get_business_data(return_move_id, sale=sale, purchase=purchase)

        data = {
            'type': type,
            'move_id': return_move_id.id,
            'sale_line_id': return_move_id.sale_line_id.id,
            'purchase_line_id': return_move_id.purchase_line_id.id,
            'partner_id': self._get_partner_id(return_move_id, sale=sale, purchase=purchase).id,
            'product_id': return_move_id.product_id.id,
            'quantity': return_move_id.quantity_done,  # 完成数量
            'business_untaxed_amount': amount,
            'avg_price': price_unit,
            'business_tax': tax,
            'business_amount': taxed_amount,
            'tax_id': [(6, 0, tax_ids.ids)],
            'measure_unit': measure_unit.id,
            'business_user_id': user_id.id,
        }
        return data

    def _get_partner_id(self, move_id, sale=False, purchase=False):
        """获取指定stock.move的对应的供应商或者客户"""
        move_id.ensure_one()

        partner_id = move_id.picking_id.partner_id
        if not partner_id and sale:
            partner_id = move_id.sale_line_id.order_partner_id
        elif not partner_id  and purchase:
            partner_id = move_id.purchase_line_id.partner_id

        if not partner_id:
            raise UserError('没有找到调拨明细对应的客户/供应商')

        return partner_id

    def _get_business_data(self, move_id, sale=False, purchase=False):
        """根据调拨明细的完成数量，计算销售金额"""
        move_id.ensure_one()
        if sale:
            try:
                percent = move_id.quantity_done / move_id.sale_line_id.product_uom_qty
            except ZeroDivisionError:
                percent = 0
            taxed_amount = move_id.sale_line_id.price_total * percent
            tax = move_id.sale_line_id.price_tax * percent
            amount = move_id.sale_line_id.price_subtotal * percent
            measure_unit = move_id.sale_line_id.product_uom
            user_id = move_id.sale_line_id.order_id.user_id
            tax_ids = move_id.sale_line_id.tax_id
            price_unit = move_id.sale_line_id.price_unit

        elif purchase:
            try:
                percent = move_id.quantity_done / move_id.purchase_line_id.product_qty
            except ZeroDivisionError:
                percent = 0
            taxed_amount = move_id.purchase_line_id.price_total * percent
            tax = move_id.purchase_line_id.price_tax * percent
            amount = move_id.purchase_line_id.price_subtotal * percent
            measure_unit = move_id.purchase_line_id.product_uom
            user_id = move_id.purchase_line_id.order_id.user_id
            tax_ids = move_id.purchase_line_id.taxes_id
            price_unit = move_id.purchase_line_id.price_unit
        else:
            raise UserError('无法获取到对应的业务金额')

        return taxed_amount, tax, amount, measure_unit, user_id, tax_ids, price_unit

    def _filter_proper_lines(self, move_ids, sale_lines, purchase_lines):
        """筛选出可以生成发票明细的调拨、销售、采购等明细行，主要是剔除那些已生成发票明细、单据未完成的。
        还会拆分出退货的调拨明细"""
        # for move_id in move_ids:
        #     if move_id.pre_account_line_ids or move_id.picking_id.state != 'done' or move_id.sale_line_id.pre_account_line_ids or move_id.purchase_line_id.pre_account_line_ids:
        #         raise UserError(f'因为调拨明细{move_id.product_id.display_name}已生成发票明细或者调拨单未完成，不能生成发票明细！')
        #
        # for sale_line in sale_lines:
        #     if sale_line.pre_account_line_ids or sale_line.state not in ['done', 'sale']:
        #         raise UserError(f'因为销售明细{sale_line.product_id.display_name}已生成发票明细或者销售单未完成，不能生成发票明细！')
        #
        # for purchase_line in purchase_lines:
        #     if purchase_line.pre_account_line_ids or purchase_line.state not in ['done', 'purchase']:
        #         raise UserError(f'因为采购明细{purchase_line.product_id.display_name}已生成发票明细或者采购单未完成，不能生成发票明细！')

        if not move_ids:
            move_ids = self.env['stock.move']
        if not sale_lines:
            sale_lines = self.env['sale.order.line']
        if not purchase_lines:
            purchase_lines = self.env['purchase.order.line']

        return_move_ids = move_ids.filtered(lambda x: x._is_return())   # 退货调拨明细
        move_ids -= return_move_ids

        subtract_moves = self.env['stock.move']
        # 这里为了防止重复生成发票明细。通过检查剩余未开票数量和本次入库数量来防止重复开票。但仍然存在漏洞，特别是一个采购、销售明细在一个入库单上分成了多行
        for move_id in move_ids:
            if move_id.picking_id.state != 'done' or move_id.pre_account_line_ids:
                subtract_moves |= move_id
            elif move_id.sale_line_id.pre_account_line_ids:
                if (move_id.sale_line_id.product_uom_qty - sum(move_id.sale_line_id.pre_account_line_ids.mapped('quantity'))) < move_id.quantity_done:
                    subtract_moves |= move_id
            elif move_id.purchase_line_id.pre_account_line_ids:
                if (move_id.purchase_line_id.product_qty - sum(move_id.purchase_line_id.pre_account_line_ids.mapped('quantity'))) < move_id.quantity_done:
                    subtract_moves |= move_id

        move_ids -= subtract_moves

        subtract_return_moves = self.env['stock.move']
        for return_move in return_move_ids:
            if return_move.pre_account_line_ids:
                subtract_return_moves |= return_move
        return_move_ids -= subtract_return_moves

        subtract_sale_lines = self.env['sale.order.line']
        for sale_line in sale_lines:
            if sale_line.pre_account_line_ids or sale_line.state not in ['done', 'sale']:
                subtract_sale_lines |= sale_line
        sale_lines -= subtract_sale_lines

        subtract_purchase_lines = self.env['purchase.order.line']
        for purchase_line in purchase_lines:
            if purchase_line.pre_account_line_ids or purchase_line.state not in ['done', 'purchase']:
                subtract_purchase_lines |= purchase_line
        purchase_lines -= subtract_purchase_lines

        return move_ids, return_move_ids, sale_lines, purchase_lines









